aboutsummaryrefslogtreecommitdiff
path: root/pages/en/profile/[user].js
diff options
context:
space:
mode:
Diffstat (limited to 'pages/en/profile/[user].js')
-rw-r--r--pages/en/profile/[user].js423
1 files changed, 423 insertions, 0 deletions
diff --git a/pages/en/profile/[user].js b/pages/en/profile/[user].js
new file mode 100644
index 0000000..6bc804e
--- /dev/null
+++ b/pages/en/profile/[user].js
@@ -0,0 +1,423 @@
+import { getServerSession } from "next-auth";
+import { authOptions } from "../../api/auth/[...nextauth]";
+import Navbar from "../../../components/navbar";
+import Image from "next/image";
+import Link from "next/link";
+import Head from "next/head";
+import { useState } from "react";
+
+export default function MyList({ media, sessions, user, time }) {
+ const [listFilter, setListFilter] = useState("all");
+ const [visible, setVisible] = useState(false);
+
+ const filterMedia = (status) => {
+ if (status === "all") {
+ return media;
+ }
+ return media.filter((m) => m.name === status);
+ };
+ return (
+ <>
+ <Head>
+ <title>My Lists</title>
+ </Head>
+ <Navbar />
+ <div className="w-screen lg:flex justify-between lg:px-10 xl:px-32 py-5 relative">
+ <div className="lg:w-[30%] h-full mt-12 lg:mr-10 grid gap-5 mx-3 lg:mx-0 antialiased">
+ <div className="flex items-center gap-5">
+ <Image
+ src={user.avatar.large}
+ alt="user avatar"
+ width={1000}
+ height={1000}
+ className="object-cover h-28 w-28 rounded-lg"
+ />
+ {user.bannerImage ? (
+ <Image
+ src={user.bannerImage}
+ alt="image"
+ width={1000}
+ height={1000}
+ priority
+ className="absolute w-screen h-[240px] object-cover -top-[7.75rem] left-0 -z-50 brightness-[65%]"
+ />
+ ) : (
+ <div className="absolute w-screen h-[240px] object-cover -top-[7.75rem] left-0 -z-50 brightness-[65%] bg-image" />
+ )}
+ <h1 className="font-karla font-bold text-2xl pt-7">{user.name}</h1>
+ </div>
+ <div className="flex items-center justify-between">
+ <div className="flex gap-2 text-sm font-karla">
+ Created At :
+ <UnixTimeConverter unixTime={user.createdAt} />
+ </div>
+ {sessions && user.name === sessions?.user.name ? (
+ <Link
+ href={"https://anilist.co/settings/"}
+ className="flex items-center gap-2 p-1 px-2 ring-[1px] antialiased ring-txt rounded-lg text-xs font-karla hover:bg-txt hover:shadow-lg group"
+ >
+ <svg
+ xmlns="http://www.w3.org/2000/svg"
+ fill="none"
+ viewBox="0 0 24 24"
+ strokeWidth={1.5}
+ stroke="currentColor"
+ className="w-4 h-4 group-hover:stroke-black"
+ >
+ <path
+ strokeLinecap="round"
+ strokeLinejoin="round"
+ d="M9.53 16.122a3 3 0 00-5.78 1.128 2.25 2.25 0 01-2.4 2.245 4.5 4.5 0 008.4-2.245c0-.399-.078-.78-.22-1.128zm0 0a15.998 15.998 0 003.388-1.62m-5.043-.025a15.994 15.994 0 011.622-3.395m3.42 3.42a15.995 15.995 0 004.764-4.648l3.876-5.814a1.151 1.151 0 00-1.597-1.597L14.146 6.32a15.996 15.996 0 00-4.649 4.763m3.42 3.42a6.776 6.776 0 00-3.42-3.42"
+ />
+ </svg>
+ <span className="group-hover:text-black">Edit Profile</span>
+ </Link>
+ ) : null}
+ </div>
+ <div className="bg-secondary lg:min-h-[160px] text-xs rounded-md p-4 font-karla">
+ <div>
+ {user.about ? (
+ <div dangerouslySetInnerHTML={{ __html: user.about }} />
+ ) : (
+ "No description created."
+ )}
+ </div>
+ </div>
+
+ <div className="bg-secondary font-karla rounded-md h-20 p-1 grid grid-cols-3 place-items-center text-center text-txt">
+ <div>
+ <h1 className="text-action font-bold">
+ {user.statistics.anime.episodesWatched}
+ </h1>
+ <h2 className="text-sm">Total Episodes</h2>
+ </div>
+ <div>
+ <h1 className="text-action font-bold">
+ {user.statistics.anime.count}
+ </h1>
+ <h2 className="text-sm">Total Anime</h2>
+ </div>
+ {time?.days ? (
+ <div>
+ <h1 className="text-action font-bold">{time.days}</h1>
+ <h2 className="text-sm">Days Watched</h2>
+ </div>
+ ) : (
+ <div>
+ <h1 className="text-action font-bold">{time.hours}</h1>
+ <h2 className="text-sm">hours</h2>
+ </div>
+ )}
+ </div>
+ {media.length !== 0 && (
+ <div className="font-karla grid gap-4">
+ <div className="flex md:justify-normal justify-between items-center">
+ <div className="flex items-center gap-3">
+ <h1>Lists Filter</h1>
+ <svg
+ xmlns="http://www.w3.org/2000/svg"
+ fill="none"
+ viewBox="0 0 24 24"
+ strokeWidth={1.5}
+ stroke="currentColor"
+ className="w-[20px] h-[20px]"
+ >
+ <path
+ strokeLinecap="round"
+ strokeLinejoin="round"
+ d="M12 3c2.755 0 5.455.232 8.083.678.533.09.917.556.917 1.096v1.044a2.25 2.25 0 01-.659 1.591l-5.432 5.432a2.25 2.25 0 00-.659 1.591v2.927a2.25 2.25 0 01-1.244 2.013L9.75 21v-6.568a2.25 2.25 0 00-.659-1.591L3.659 7.409A2.25 2.25 0 013 5.818V4.774c0-.54.384-1.006.917-1.096A48.32 48.32 0 0112 3z"
+ />
+ </svg>
+ </div>
+ <div
+ className="md:hidden bg-secondary p-1 rounded-md cursor-pointer"
+ onClick={() => setVisible(!visible)}
+ >
+ <svg
+ xmlns="http://www.w3.org/2000/svg"
+ fill="none"
+ viewBox="0 0 24 24"
+ strokeWidth={1.5}
+ stroke="currentColor"
+ className="w-6 h-6"
+ >
+ <path
+ strokeLinecap="round"
+ strokeLinejoin="round"
+ d="M6.75 12a.75.75 0 11-1.5 0 .75.75 0 011.5 0zM12.75 12a.75.75 0 11-1.5 0 .75.75 0 011.5 0zM18.75 12a.75.75 0 11-1.5 0 .75.75 0 011.5 0z"
+ />
+ </svg>
+ </div>
+ </div>
+ <ul
+ className={`group md:grid gap-1 text-sm ${
+ visible ? "" : "hidden"
+ }`}
+ >
+ <li
+ onClick={() => setListFilter("all")}
+ className={`p-2 cursor-pointer hover:text-action ${
+ listFilter === "all" && "bg-secondary text-action"
+ }`}
+ >
+ <h1 className={`cursor-pointer hover:text-action`}>
+ Show All
+ </h1>
+ </li>
+ {media.map((item) => (
+ <li
+ key={item.name}
+ onClick={() => setListFilter(item.name)}
+ className={`cursor-pointer hover:text-action flex gap-2 p-2 duration-200 ${
+ item.name === listFilter && "bg-secondary text-action"
+ }`}
+ >
+ <h1 className="">{item.name}</h1>
+ <div className="text-gray-400 opacity-0 invisible duration-200 transition-all group-hover:visible group-hover:opacity-100">
+ ({item.entries.length})
+ </div>
+ </li>
+ ))}
+ </ul>
+ </div>
+ )}
+ </div>
+
+ <div className="lg:w-[75%] grid gap-10 my-12 lg:pt-16">
+ {media.length !== 0 ? (
+ filterMedia(listFilter).map((item, index) => {
+ return (
+ <div key={index} className="flex flex-col gap-5 mx-3">
+ <h1 className="font-karla font-bold text-xl">{item.name}</h1>
+ <table className="bg-secondary rounded-lg">
+ <thead>
+ <tr>
+ <th className="font-bold text-xs py-3 text-start pl-10 lg:w-[75%] w-[65%]">
+ Title
+ </th>
+ <th className="font-bold text-xs py-3">Score</th>
+ <th className="font-bold text-xs py-3">Progress</th>
+ </tr>
+ </thead>
+ <tbody className="">
+ {item.entries.map((item) => {
+ return (
+ <tr
+ key={item.mediaId}
+ className="hover:bg-orange-400 duration-150 ease-in-out group relative"
+ >
+ <td className="font-medium py-2 pl-2 rounded-l-lg">
+ <div className="flex items-center gap-2">
+ {item.media.status === "RELEASING" ? (
+ <span className="dot group-hover:invisible bg-green-500 shrink-0" />
+ ) : item.media.status === "NOT_YET_RELEASED" ? (
+ <span className="dot group-hover:invisible bg-red-500 shrink-0" />
+ ) : (
+ <span className="dot group-hover:invisible shrink-0" />
+ )}
+ <Image
+ src={item.media.coverImage.large}
+ alt="Cover Image"
+ width={500}
+ height={500}
+ className="object-cover rounded-md w-10 h-10 shrink-0"
+ />
+ <div className="absolute -top-10 -left-40 invisible lg:group-hover:visible">
+ <Image
+ src={item.media.coverImage.large}
+ alt={item.media.id}
+ width={1000}
+ height={1000}
+ className="object-cover h-[186px] w-[140px] shrink-0 rounded-md"
+ />
+ </div>
+ <Link
+ href={`/en/anime/${item.media.id}`}
+ className="font-semibold font-karla pl-2 text-sm line-clamp-1"
+ title={item.media.title.romaji}
+ >
+ {item.media.title.romaji}
+ </Link>
+ </div>
+ </td>
+ <td className="text-center text-xs text-txt">
+ {item.score === 0 ? null : item.score}
+ </td>
+ <td className="text-center text-xs text-txt rounded-r-lg">
+ {item.progress === item.media.episodes
+ ? item.progress
+ : item.media.episodes === null
+ ? item.progress
+ : `${item.progress}/${item.media.episodes}`}
+ </td>
+ </tr>
+ );
+ })}
+ </tbody>
+ </table>
+ </div>
+ );
+ })
+ ) : (
+ <div className="w-screen lg:w-full flex-center flex-col gap-5">
+ {user.name === sessions?.user.name ? (
+ <p className="text-center font-karla font-bold lg:text-lg">
+ Oops!<br></br> Looks like you haven't watch anything yet.
+ </p>
+ ) : (
+ <p className="text-center font-karla font-bold lg:text-lg">
+ Oops!<br></br> It looks like this user haven't watch anything
+ yet.
+ </p>
+ )}
+ <Link
+ href="/en/search/anime"
+ className="flex gap-2 text-sm ring-1 ring-action p-2 rounded-lg font-karla"
+ >
+ <svg
+ xmlns="http://www.w3.org/2000/svg"
+ fill="none"
+ viewBox="0 0 24 24"
+ strokeWidth={1.5}
+ stroke="currentColor"
+ className="w-5 h-5"
+ >
+ <path
+ strokeLinecap="round"
+ strokeLinejoin="round"
+ d="M21 21l-5.197-5.197m0 0A7.5 7.5 0 105.196 5.196a7.5 7.5 0 0010.607 10.607z"
+ />
+ </svg>
+ <span>Start Watching</span>
+ </Link>
+ </div>
+ )}
+ </div>
+ </div>
+ </>
+ );
+}
+
+export async function getServerSideProps(context) {
+ const session = await getServerSession(context.req, context.res, authOptions);
+ const query = context.query;
+
+ const response = await fetch("https://graphql.anilist.co/", {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({
+ query: `
+ query ($username: String, $status: MediaListStatus) {
+ MediaListCollection(userName: $username, type: ANIME, status: $status, sort: SCORE_DESC) {
+ user {
+ id
+ name
+ about (asHtml: true)
+ createdAt
+ avatar {
+ large
+ }
+ statistics {
+ anime {
+ count
+ episodesWatched
+ meanScore
+ minutesWatched
+ }
+ }
+ bannerImage
+ mediaListOptions {
+ animeList {
+ sectionOrder
+ }
+ }
+ }
+ lists {
+ status
+ name
+ entries {
+ id
+ mediaId
+ status
+ progress
+ score
+ media {
+ id
+ status
+ title {
+ english
+ romaji
+ }
+ episodes
+ coverImage {
+ large
+ }
+ }
+ }
+ }
+ }
+ }
+ `,
+ variables: {
+ username: query.user,
+ },
+ }),
+ });
+
+ const data = await response.json();
+
+ const get = data.data.MediaListCollection;
+ const sectionOrder = get?.user.mediaListOptions.animeList.sectionOrder;
+
+ if (!sectionOrder) {
+ return {
+ notFound: true,
+ };
+ }
+
+ const prog = get.lists;
+
+ function getIndex(status) {
+ const index = sectionOrder.indexOf(status);
+ return index === -1 ? sectionOrder.length : index;
+ }
+
+ prog.sort((a, b) => getIndex(a.name) - getIndex(b.name));
+
+ const user = get.user;
+
+ const time = convertMinutesToDays(user.statistics.anime.minutesWatched);
+
+ return {
+ props: {
+ media: prog,
+ sessions: session,
+ user: user,
+ time: time,
+ },
+ };
+}
+
+function UnixTimeConverter({ unixTime }) {
+ const date = new Date(unixTime * 1000); // multiply by 1000 to convert to milliseconds
+ const formattedDate = date.toISOString().slice(0, 10); // format date to YYYY-MM-DD
+
+ return <p>{formattedDate}</p>;
+}
+
+function convertMinutesToDays(minutes) {
+ const hours = minutes / 60;
+ const days = hours / 24;
+
+ if (days >= 1) {
+ return days % 1 === 0
+ ? { days: `${parseInt(days)}` }
+ : { days: `${days.toFixed(1)}` };
+ } else {
+ return hours % 1 === 0
+ ? { hours: `${parseInt(hours)}` }
+ : { hours: `${hours.toFixed(1)}` };
+ }
+}